package com.java.lambda;


import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @program: java_basic_knowledge
 * @description:
 * @author: CaoYong
 * @create: 2021-04-09 09:12
 **/
public class LambdaKnowledge {
    /**
     * 在使用lambda表达式之前
     * 有时如果想要传递的参数是一个方法而不是对象，为了实现这一点只能通过在接口定义方法，通过传递接口的方式实现
     * 如下File对象的listFile方法需要的其实不是FilenameFilter对象而是accept()方法，但是只能通过传递FilenameFilter接口并通过匿名内部类实现accept()方法来实现
     * Lambda表达式与匿名内部类很像，主要是简单化了语法，但是起与匿名内部类的不同就是Java会为匿名内部类生成一个真正的类，而Lambda表达式则不会，因此Lambda表达式的性能会比匿名内部类更好
     * Lambda表达式的类型是函数式接口
     */
    public static class WhyNeedLambda {
        File f = new File(".");
        File[] files = f.listFile(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                if (name.endsWith(".txt")) {
                    return true;
                } else {
                    return false;
                }
            }
        });

        /**
         * 在使用了lambda表达式后，方法可以以lambda表达式的方式替换匿名内部类实现
         */
        File[] fileArray = f.listFile((File file, String name) -> {
            return name.endsWith(".txt");
        });

        // 当主体代码只有一行时，花括号和return语句都可以省略，整个表达式的值就是函数的返回值
        File[] fileArray1 = f.listFile((File file, String name) -> name.endsWith(".txt"));

        // lambda表达式中方法参数的类型也可以省略，因为Java可以根据接口的定义自行推断
        File[] fileArray2 = f.listFile( (file, name) -> name.endsWith(".txt"));
    }

    /**
     * 函数式接口
     * Java8引入函数式接口的概念，函数式接口也是接口，但是只能有一个抽象方法
     * 比如Runnable就是一个函数式接口，只有一个抽象方法run(), 并由注解@FunctionalInterface修饰，改注解保证如果定义了超过一个抽象方法则编译报错
     */
    public  static void whatIsFunctionalInterface() {
        Runnable task = () -> System.out.println("hello world");
    }


    public static void main(String[] args) {
        whatIsFunctionalInterface();
        List<Student> students = new ArrayList<>();
        students.add(new Student("张三", 92));
        students.add(new Student("李四", 80));
        students.add(new Student("王五", 100));

        students = filter(students, t -> t.getScore() > 90);
        for (Student student : students) {
            System.out.println(student.getName());
        }

        // Student::getName是Java8引入的新语法，称为方法引用，是Lambda表达式的一种简写方法，适用于调用对象的某个方法
        // :: 分隔两个部分，前面是类名或变量名，后面是方法名，方法可以是实例方法也可以是静态方法
        List<String> collect = students.stream().map(Student::getName).collect(Collectors.toList());

        // 引用静态方法 第一个参数是类
        Supplier<String> s = Student::getCollegeName;
        Supplier<String> s1 = () -> Student.getCollegeName();

        // 引用实例方法 第一个参数是实例
        Function<Student, String> f = Student::getName;
        Function<Student, String> f1 = (Student t) -> t.getName();






    }

    /**
     * Java8定义了许多预定义的函数式接口，方便Stream中使用或者自定义使用
     * 比如Predicate就是一个预定义函数接口，只有一个抽象方法boolean test(T t)，用于做条件判断
     * 在定义filter方法时，当要讲判断方法作为参数传递时，只要用预定义函数式接口Predicate作为参数传入
     * 在具体使用filter方法时，将判断方法以lambda表达式传入
     */
    public static <E> List<E> filter(List<E> list, Predicate<E> pred) {
        List<E> retList = new ArrayList<>();
        for (E e: list) {
            if(pred.test(e)) {
                retList.add(e);
            }
        }
        return retList;
    }

    public static class Student {

        public Student(String name, int score) {
            this.name = name;
            this.score = score;
        }

        String name;
        int score;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getScore() {
            return score;
        }

        public void setScore(int score) {
            this.score = score;
        }

        public static String getCollegeName() {
            return "Lambda School";
        }
    }

    public static class File {

        private String name;

        public File() {
        }

        public File(String name) {
            this.name = name;
        }

        public File[] listFile(FilenameFilter filenameFilter) {
            boolean accept = filenameFilter.accept(this, "test");
            File[] files = new File[10];
            return files;
        }
    }

    public static interface FilenameFilter {
        boolean accept(File dir, String name);
    }
}
